package com.common.sdk.net.download.http;

import android.os.Handler;
import android.os.Looper;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;

import com.android.sohu.sdk.common.toolbox.LogUtils;
import com.android.sohu.sdk.common.toolbox.NetworkUtils;
import com.android.sohu.sdk.common.toolbox.StorageUtils;
import com.common.sdk.net.download.callback.CallbackObservable;
import com.common.sdk.net.download.callback.error.CallbackEnum;
import com.common.sdk.net.download.callback.error.DownloadState;
import com.common.sdk.net.download.db.ControlCacheAndDb;
import com.common.sdk.net.download.request.DownloadRequest;
import com.common.sdk.net.download.request.model.DownloadInfo;
import com.common.sdk.net.download.util.DownloadFilePathUtils;
import com.common.sdk.net.download.util.DownloadLog;
import com.common.sdk.net.connect.http.HttpStack;
import com.common.sdk.net.connect.http.error.AuthFailureError;
import com.common.sdk.net.connect.http.error.VolleyError;
import com.sohu.trafficstatistics.util.TrafficStatisticUtil;

import okhttp3.Response;

public class DownloadingNetwork extends AbsNetwork {

    public DownloadingNetwork(HttpStack httpStack) {
        super(httpStack);
    }

    @Override
    protected int initRequest(DownloadRequest request, int threadId, int retryCount) throws IOException, VolleyError {
        // 同时确认是否这条数据的数据库是否有相应的文件对应，如果没有重新下载

        // 都存在 因为路径问题。里面的判断也有问题
        if (request.isDownloadingDBState() && request.isHasSDFile()) {
            // 开始下载
            return noDbDataAndNoSdData(request, retryCount);
        }
        // 都不存在，联网获取数据，一般为第一次加载数据
        if (!request.isDownloadingDBState() && !request.isHasSDFile()) {
            return noDbDataAndNoSdData(request, retryCount);
        }
        // 数据库被清除，本地文件存在
        if (!request.isDownloadingDBState() && request.isHasSDFile()) {
            return DownloadState.DATABASE_ERROR_NO;
        }
        // 本地文件被删除，数据库数据存在
        if (request.isDownloadingDBState() && !request.isHasSDFile()) {
            return DownloadState.DATABASE_ERROR_NO_FILE;
        }
        // 无网导致的下载失败不上报
        int netType = NetworkUtils.getNetworkType(DownloadFilePathUtils.getContext());
        if (netType == NetworkUtils.NETWORK_TYPE_UNAVAILABLE ||
                netType == NetworkUtils.NETWORK_TYPE_UNKNOWN){
            return DownloadState.NET_ERROR_NO_NET_NO_UPLOAD;
        }
        return DownloadState.NET_ERROR_UNKNOWN;
    }

    /**
     * 下载前准备工作
     * @param request
     * @return
     * @throws IOException
     * @throws AuthFailureError
     */
    @Override
    protected int noDbDataAndNoSdData(DownloadRequest request, int retryCount) throws IOException, VolleyError {
        DownloadInfo myDownloadInfo = request.getDownloadInfo();
        DownloadLog.debug("Ready to start networking" + System.nanoTime());

        if (myDownloadInfo == null
                || (myDownloadInfo.getPackageName() == null && myDownloadInfo.getPackageName().equals(""))
                || myDownloadInfo.getVersionCode() == 0
                || (myDownloadInfo.getDownloadPath() == null && myDownloadInfo.getDownloadPath().equals(""))
                || myDownloadInfo.getDownloadPriority() == 0) {
            return DownloadState.NO_DATA;
        }
        myDownloadInfo.getDownloadPath();

        if (myDownloadInfo.getAlreadyDownloadSize() <= 0) {
            okhttpResponse = okhttpResponse(request, 0, 0);
        } else {
            okhttpResponse = okhttpResponse(request, myDownloadInfo.getAlreadyDownloadSize(),
                    myDownloadInfo.getFileSize());
        }
        // 网络连接失败重新联网有问题
        if (okhttpResponse.body() != null) {
            myDownloadInfo.startDownload();
            if (myDownloadInfo.getAlreadyDownloadSize() <= 0) {
                long fileSize =  okhttpResponse.body().contentLength();
                if(fileSize<=0){
                    return DownloadState.NET_ERROR;
                }
                myDownloadInfo.setFileSize(fileSize);

                if (!DownloadFilePathUtils.isSpaceSmall() || fileSize > StorageUtils.getAvailableSpace()) {
                    return DownloadState.SD_SPACE_SMALL;
                }
                DownloadLog.debug("Get the size success, the size of：" + fileSize + "Prepare to enter the database"
                        + System.nanoTime());
                ControlCacheAndDb.beginDownload(myDownloadInfo);
            }
            return downloading(request, okhttpResponse, retryCount);
        } else {
            DownloadLog.debug("Networking failure" + System.nanoTime());
        }
        return DownloadState.NET_ERROR_UNKNOWN;

    }

    private int downloading(DownloadRequest request, HttpResponse httpResponse) throws IOException {
        File filePath = request.getDownloadFilePath();
        DownloadInfo myDownloadInfo = request.getDownloadInfo();
        RandomAccessFile threadfile = new RandomAccessFile(filePath, "rwd");
        threadfile.setLength(myDownloadInfo.getFileSize());

        HttpEntity entity = null;
        InputStream in = null;

        byte[] buffer = mPool.getBuf(DEFAULT_POOL_SIZE);
        try {
            if (httpResponse != null) {
                entity = httpResponse.getEntity();
                if (entity != null) {
                    in = entity.getContent();
                    if (in == null) {
                        return DownloadState.NET_ERROR_DOWNLOAD_IOEXCEPTION;
                    }

                    threadfile.seek(myDownloadInfo.getAlreadyDownloadSize());
                    CallbackObservable.getIntence().notifyObservers(CallbackEnum.DID_START_DOWNLOAD_ITEM,
                            myDownloadInfo);

                    int count = 0;
                    long startTime = System.currentTimeMillis();
                    long startAlreadyDownloadSize = myDownloadInfo.getAlreadyDownloadSize();

                    while (((count = in.read(buffer)) != -1) && isNotEndDownload(myDownloadInfo) && !myDownloadInfo.isCanceled()) {
                        TrafficStatisticUtil.recordData(request.getUrlWithQueryString(), count);
                        threadfile.write(buffer, 0, count);
                        myDownloadInfo.getAlreadyDownloadSize();
                        DownloadLog.debug("lakshjfgdlajs" + "AlreadyDownloadSize:" + myDownloadInfo.getAlreadyDownloadSize()
                                + "...count:" + count+"...in.available():"+in.available());

                        myDownloadInfo.setAlreadyDownloadSize(count);
                        ControlCacheAndDb.beginDownload(myDownloadInfo);

                        long endTime = System.currentTimeMillis();
                        long endAlreadyDownloadSize = myDownloadInfo.getAlreadyDownloadSize();

                        float increaseSize = (float) Math.abs(endAlreadyDownloadSize - startAlreadyDownloadSize);
                        // 时间是否为零
                        float changeTime = (float) Math.abs(endTime - startTime);

                        if (changeTime > 1000 || endAlreadyDownloadSize == count) {
                            float speed = (increaseSize * 1000) / changeTime;
                            myDownloadInfo.setSpeed(speed);
                            long downloadPercentage = (endAlreadyDownloadSize * 100) / myDownloadInfo.getFileSize();
                            myDownloadInfo.setDownloadPercentage((int) downloadPercentage);
                            startAlreadyDownloadSize = endAlreadyDownloadSize;
                            startTime = endTime;
                            CallbackObservable.getIntence().notifyObservers(CallbackEnum.PROGRESS_DOWNLOAD, myDownloadInfo);

                            DownloadLog.debug(myDownloadInfo.toString() + "  aplkDownloadModel getAlreadyDownloadSize : "
                                    + myDownloadInfo.getAlreadyDownloadSize() + " count:" + count + "buffer.length:"
                                    + buffer.length + "myDownloadInfo.getAlreadyDownloadSize()"
                                    + myDownloadInfo.getAlreadyDownloadSize() + "Download the rest："
                                    + (myDownloadInfo.getFileSize() - myDownloadInfo.getAlreadyDownloadSize()));
                        }
                    }
                    return endDownloadState(myDownloadInfo);
                }
            }
        } finally {
            mPool.returnBuf(buffer);
            try {
                if (in != null) {
                    in.close();
                }
                threadfile.close();
                if (entity != null) {
                    entity.consumeContent();
                }
            } catch (Exception e) {
                LogUtils.e(e);
                in = null;
                threadfile = null;
                entity = null;
            }
        }
        return DownloadState.NET_ERROR_UNKNOWN;
    }


    private int downloading(final DownloadRequest request , Response response, int retryCount) {

        if (retryCount <=0 ){
            return result;
        }

        final File filePath = request.getDownloadFilePath();
        final DownloadInfo myDownloadInfo = request.getDownloadInfo();
        byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
        int len = 0;
        InputStream is = null;
        FileDescriptor fd;
        final BufferedOutputStream fos = null;
        try {
            is = response.body().byteStream();
            long total = myDownloadInfo.getFileSize();
//            myDownloadInfo.setFileSize(total);
//            File file = filePath;
            RandomAccessFile threadfile = new RandomAccessFile(filePath, "rwd");
            threadfile.setLength(total);
            fd = threadfile.getFD();
//            fos = new BufferedOutputStream(new FileOutputStream(fd));
            threadfile.seek(myDownloadInfo.getAlreadyDownloadSize());

            long sum = myDownloadInfo.getAlreadyDownloadSize();
            long startTime = System.currentTimeMillis();
            long startAlreadyDownloadSize = myDownloadInfo.getAlreadyDownloadSize();

            while ((len = is.read(buf)) != -1 && isNotEndDownload(myDownloadInfo) && !myDownloadInfo.isCanceled()) {
                TrafficStatisticUtil.recordData(request.getUrlWithQueryString(), len);
                threadfile.write(buf, 0, len);
                sum += len;
//              int progress = (int) (sum * 1.0f / total * 100);
                long endTime = System.currentTimeMillis();
                long endAlreadyDownloadSize = sum;

                float increaseSize = (float) Math.abs(endAlreadyDownloadSize - startAlreadyDownloadSize);
                // 时间是否为零
                float changeTime = (float) Math.abs(endTime - startTime);

                if (changeTime >= 1000 || endAlreadyDownloadSize == total) {

                    float speed = (increaseSize * 1000) / changeTime;
                    long downloadPercentage = (endAlreadyDownloadSize * 100) / total;
                    myDownloadInfo.setAlreadyDownloadSize(endAlreadyDownloadSize);
                    myDownloadInfo.setSpeed(speed);
                    myDownloadInfo.setDownloadPercentage((int) downloadPercentage);
                    ControlCacheAndDb.beginDownload(myDownloadInfo);
                    CallbackObservable.getIntence().notifyObservers(CallbackEnum.PROGRESS_DOWNLOAD, myDownloadInfo);

                    DownloadLog.debug("increaseSize :" + increaseSize
                            + " changeTime " + changeTime);

                    DownloadLog.debug(" downloadPercentage:" + downloadPercentage
                            + " speed: " + speed
                            + " AlreadyDownloadSize : " + endAlreadyDownloadSize
                            + "Download the rest：" + (total - endAlreadyDownloadSize));
                    startAlreadyDownloadSize = endAlreadyDownloadSize;
                    startTime = endTime;
                }
            }
            if (fos != null){
                fos.flush();
            }
            fd.sync();


            // 下载完成
            result = endDownloadState(myDownloadInfo);
        } catch (Exception e) {
            DownloadLog.debug("downloading onException");
            DownloadLog.e(e);
            retryDownloadForException(request, retryCount);

        } finally {
            recycleStream(is, fos);
        }
        return result;

    }


    /**
     * 下载开始后判断状态  与下面对应的
     * @param myDownloadInfo
     * @return
     */
    private boolean isNotEndDownload(DownloadInfo myDownloadInfo) {
        return !myDownloadInfo.isPauseDownload() && !myDownloadInfo.isStopDownload()
                && !myDownloadInfo.isDeleteDownload() && !myDownloadInfo.isSilenceDownloadPause();
    }

    /**
     * 下载结束后判断状态 与上面对应的
     * @param myDownloadInfo
     * @return
     */
    private int endDownloadState(DownloadInfo myDownloadInfo) {
        if (myDownloadInfo.isDeleteDownload()) {
            return DownloadState.DOWNLOAD_THREAD_DELETY_SUCCESS;
        }
        // 循环结束。检查下载是否有问题
        if (myDownloadInfo.getAlreadyDownloadSize() == myDownloadInfo.getFileSize()
                || myDownloadInfo.getAlreadyDownloadSize() == myDownloadInfo.getFileSize() + 1) {
            myDownloadInfo.finishDownload();
            return DownloadState.DOWNLOAD_SUCCESS;
        } else {
            if (myDownloadInfo.isPauseDownload()) {
                return DownloadState.DOWNLOAD_PAUSE_SUCCESS;
            } else if (myDownloadInfo.isStopDownload()) {
                return DownloadState.DOWNLOAD_STOP_SUCCESS;
            } else if (myDownloadInfo.isSilenceDownloadPause()) {
                return DownloadState.DOWNLOAD_SILENCE_PAUSE_SUCCESS;
            }
        }
        return DownloadState.NET_ERROR_UNKNOWN;
    }

}
